package com.wpml.cloud.classroom.netty;

import com.wpml.cloud.classroom.constant.NettyStatus;
import com.wpml.cloud.classroom.handler.MsgHandler;
import com.wpml.cloud.classroom.handler.NettyWsChannelInboundHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description
 * @Date 2021-05-13
 * @Author wangpei
 **/
public class NettyServer {

    private static final Logger log = LogManager.getLogger(NettyServer.class);

    ServerBootstrap bootstrap = null;
    EventLoopGroup bossGroup = null;
    EventLoopGroup workerGroup = null;
    ChannelFuture future = null;

    private int bossGroupNum = 1;
    private int workerGroupNum = Runtime.getRuntime().availableProcessors() * 2;
    //状态 0：初始化 1：已经启动 2：已关闭
    private volatile NettyStatus status = NettyStatus.INIT;
    private NettyShutdownThread nettyShutdownThread;

    @Autowired
    private List<MsgHandler> msgHandlerList;


    public void startServer() {
        bootstrap = new ServerBootstrap();
        bossGroup = new NioEventLoopGroup(bossGroupNum);
        workerGroup = new NioEventLoopGroup(workerGroupNum);

        try {
            bootstrap.group(bossGroup, workerGroup);
            bootstrap.channel(NioServerSocketChannel.class);
            bootstrap.localAddress(8050);
            //4.设置通道参数
            bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
            bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
            //设置队列大小
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
            //设置实时
            bootstrap.option(ChannelOption.TCP_NODELAY, true);

            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline p = ch.pipeline();
                    p.addLast(new HttpServerCodec());
                    p.addLast(new HttpObjectAggregator(1024 * 64));
                    p.addLast(new WebSocketServerProtocolHandler("/websocket"));
                    p.addLast(new NettyWsChannelInboundHandler(msgHandlerList.stream().collect(Collectors.toMap(MsgHandler::getMsgId, h -> h))));
                }
            });
            future = bootstrap.bind().sync();

            if (future.isSuccess()) {
                log.info("netty server start success， port：" + future.channel().localAddress());
            }

            // 注册关闭钩子    jvm关闭时等待钩子关闭
            nettyShutdownThread = new NettyShutdownThread(bossGroup, workerGroup);
            Runtime.getRuntime().addShutdownHook(nettyShutdownThread);

            status = NettyStatus.STARTED;

            // 等待服务器信道关闭,
            // 也就是不要立即退出应用程序, 让应用程序可以一直提供服务
            future.channel().closeFuture().sync();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    private void stop() {
        status = NettyStatus.STOP;
        if (future != null && future.channel() != null) {
            future.channel().close();
            Runtime.getRuntime().removeShutdownHook(nettyShutdownThread);
        }
    }


    static class NettyShutdownThread extends Thread {

        private static final Logger log = LogManager.getLogger(NettyShutdownThread.class);

        EventLoopGroup bossGroup = null;
        EventLoopGroup workerGroup = null;

        public NettyShutdownThread(EventLoopGroup bossGroup, EventLoopGroup workerGroup) {
            this.bossGroup = bossGroup;
            this.workerGroup = workerGroup;
        }

        @Override
        public void run() {
            try {
                log.info("workerGroup shutdown");
                if (workerGroup != null) {
                    workerGroup.shutdownGracefully();
                }
            } catch (Exception e) {
                log.error("workerGroup shutdown error", e);
            }

            try {
                log.info("bossGroup shutdown");
                if (bossGroup != null) {
                    bossGroup.shutdownGracefully();
                }
            } catch (Exception e) {
                log.error("bossGroup shutdown error", e);
            }
        }
    }
}


